﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Data;
using StyleMVVM.Logging;

#if NETFX_CORE
using Windows.Foundation;
using Windows.Foundation.Metadata;

#endif

namespace StyleMVVM.Validation.Impl
{
	internal class InstanceValidationContext : BaseValidationContext
	{
		private static readonly string supplemental = typeof(InstanceValidationContext).FullName;

		public InstanceValidationContext(object validationObject,
		                                 [ReadOnlyArray] string[] standards,
		                                 IValidationService validationService,
		                                 IValidationStandardsSelector standardsSelector,
		                                 IReflectionService reflectionService,
		                                 bool monitor,
		                                 bool shared)
			: base(validationObject, standards, validationService, standardsSelector, reflectionService, monitor, shared)
		{
			INotifyPropertyChanged notifyObject = ValidationObject as INotifyPropertyChanged;

			if (notifyObject != null && monitor)
			{
				notifyObject.PropertyChanged += OnPropertyChanged;
			}
		}

		private void OnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
		{
			IInternalValidationContext oldContext;

			if (childValidationContexts != null &&
			    childValidationContexts.TryGetValue(propertyChangedEventArgs.PropertyName, out oldContext))
			{
				object newValue = reflectionService.GetPropertyValue(sender, propertyChangedEventArgs.PropertyName);

				if (oldContext != null)
				{
					oldContext.SetParent(null, propertyChangedEventArgs.PropertyName);
				}

				if (newValue != null)
				{
					IInternalValidationContext newChildContext =
						validationService.GetValidationContext(newValue, monitor, shared) as IInternalValidationContext;

					if (newChildContext != null)
					{
						newChildContext.Standards = standards;

						newChildContext.SetParent(this, propertyChangedEventArgs.PropertyName);

						childValidationContexts[propertyChangedEventArgs.PropertyName] = newChildContext;

						newChildContext.Validate();
					}
				}
			}

			try
			{
				RunPropertyRules(sender, propertyChangedEventArgs.PropertyName);
			}
			catch (Exception exp)
			{
				Logger.Error("Exception thrown while processing property change: " + propertyChangedEventArgs.PropertyName,
				             supplemental,
				             exp);
			}
		}

		public override void AddRules(IEnumerable<IValidationRule> validationRules)
		{
			InternalAddRules(ValidationObject, validationRules);
		}

		public override void RemoveRules(IEnumerable<IValidationRule> validationRules)
		{
			InternalRemoveRule(ValidationObject, validationRules);
		}

		public override void Validate()
		{
			object target = ValidationObject;

			if (target != null)
			{
				RunAllRules(target);
			}

			if (childValidationContexts != null)
			{
				foreach (KeyValuePair<string, IInternalValidationContext> internalValidationContext in childValidationContexts)
				{
					if (internalValidationContext.Value != null)
					{
						internalValidationContext.Value.Validate();
					}
				}
			}
		}
	}
}